﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Neat;
using Neat.Components;
using Neat.Mathematics;
using Neat.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Sectors
{
    public class Sector
    {
        public enum SectorTypes
        {
            Normal, //Clips to tallest entity, crushes nothing
            PassableCeiling,
            PassableFloor,
            Passable,
            Crushing
        }
        NeatGame game;
        World world;

        string _name = "generic";
        float _floorHeight = -100;
        float _ceilingHeight = 100;
        string _floorTexture = "generic_floor";
        string _ceilingTexture = "generic_ceiling";
        Vector2 _floorUV = Vector2.Zero;
        Vector2 _ceilingUV = Vector2.Zero;
        SectorTypes _sectorType = SectorTypes.Normal;
        float _damagePoints = 0;
        string _lowerSector = "";
        string _upperSector = "";
        float _gravityRate;
        int _idleTime = 5; //update every 5 frames
        float _lightIntensity = 1.00f; //light intensity; diffuse color will be multiplied by it


        public string Name { get { return _name; } set { _name = value; } }
        public float FloorHeight { get { return _floorHeight; } set { _floorHeight = value; } }
        public float CeilingHeight { get { return _ceilingHeight; } set { _ceilingHeight = value; } }
        public string FloorTexture { get { return _floorTexture; } set { _floorTexture = value; } }
        public string CeilingTexture { get { return _ceilingTexture; } set { _ceilingTexture = value; } }
        public Vector2 FloorUV { get { return _floorUV; } set { _floorUV = value; } }
        public Vector2 CeilingUV { get { return _ceilingUV; } set { _ceilingUV = value; } }
        public SectorTypes SectorType { get { return _sectorType; } set { _sectorType = value; } }
        public float DamagePoints { get { return _damagePoints; } set { _damagePoints = value; } }
        public string LowerSector { get { return _lowerSector; } set { _lowerSector = value; } }
        public string UpperSector { get { return _upperSector; } set { _upperSector = value; } }
        public float GravityRate { get { return _gravityRate; } set { _gravityRate = value; } }
        public int IdleTime { get { return _idleTime; } set { _idleTime = value; } }
        public float LightIntensity { get { return _lightIntensity; } set { _lightIntensity = value; } }
                
        public Action<Sector> OnEnterAction;
        public Action<Sector> OnLeaveAction;
        public Action<Sector> IdleAction;
        public string OnEnterScript;
        public string OnLeaveScript;
        public string IdleScript;

        public Polygon Mesh;
        public List<Vector2> Vertices;
        public List<Triangle> Triangles;
        public List<Wall> Walls;

        FloorCeilingMesh model;

        public Sector(World world, float _fh, float _ch, string _ft, string _ct, Vector2 _fuv, Vector2 _cuv, string _us, string _ls, float _dp, int _it, 
            float _li, SectorTypes _st, string _n, List<Vector2> _vs)
        {
            FloorHeight = _fh;
            CeilingHeight = _ch;
            FloorTexture = _ft;
            CeilingTexture = _ct;
            FloorUV = _fuv;
            CeilingUV = _cuv;
            UpperSector = _us;
            LowerSector = _ls;
            DamagePoints = _dp;
            IdleTime = _it;
            LightIntensity = _li;
            SectorType = _st;
            Name = _n;
            this.world = world;
            Mesh = new Polygon(_vs);
            Vertices = Mesh.Vertices;
            Mesh.AutoTriangulate = true;
            Mesh.Triangulate();
            CreateWalls(true);
        }

        public Sector(World world, Polygon mesh, float floor, float ceiling)
        {
            this.world = world;
            Mesh = mesh;
            FloorHeight = floor;
            CeilingHeight = ceiling;
            Vertices = mesh.Vertices;
            mesh.Triangulate();
            CreateWalls(true);
        }

        public Sector(NeatGame game, World world, Polygon mesh, float floor, float ceiling)
        {
            this.game = game;
            this.world = world;
            Mesh = mesh;
            FloorHeight = floor;
            CeilingHeight = ceiling;
            Vertices = mesh.Vertices;
            Mesh.Triangulate();
            Triangles = mesh.Triangles;
            CreateWalls(false);
            CreateModel();
        }

        public void SetParent(NeatGame game, World world)
        {
            this.game = game;
            this.world = world;
            Mesh.Triangulate();
            CreateWalls(false);
        }
        public void CreateWalls(bool standalone)
        {
            Walls = new List<Wall>();
            foreach (var e in Mesh.GetEdges())
            {
                Wall w = world.GetWall(e);
                if (w != null)
                {
                    if (w.SectorA != this && w.SectorB != this)
                    {
                        if (w.SectorA != null)
                            w.SectorB = this;
                        else
                            w.SectorA = this;
                    }
                    Walls.Add(w);
                }
                else
                {
                    if (standalone)
                        Walls.Add(new Wall(e, this));
                    else
                        Walls.Add(new Wall(game, e, this));
                }
            }
        }

        public void CreateModel()
        {
            model = new FloorCeilingMesh(game, Mesh, FloorTexture, CeilingTexture, FloorHeight, CeilingHeight, FloorUV, CeilingUV);
            foreach (var item in Walls)
            {
                if (item != null)
                {
                    //item.CreateModel();
                    item.Game = game;
                    if (item.SectorA != this && item.SectorB != this)
                    {
                        if (item.SectorA == null)
                            item.SectorA = this;
                        else // if (item.SectorB == null)
                            item.SectorB = this;
                    }
                }
            }
        }

        public void Render(GameTime gameTime, BasicEffect effect)
        {
            if (model == null) return;
            effect.DiffuseColor = new Vector3(LightIntensity);
            model.Draw(effect);
            foreach (var item in Walls)
                item.Render(effect);
        }
        #region Console
        Neat.Components.Console console;
        public void AttachToConsole(Neat.Components.Console console)
        {
            this.console = console;
            console.AddCommand("ws_name", ws_name);
            console.AddCommand("ws_floor", ws_floor);
            console.AddCommand("ws_ceiling", ws_ceiling);
            console.AddCommand("ws_floortexture", ws_floortexture);
            console.AddCommand("ws_ceilingtexture", ws_ceilingtexture);
            console.AddCommand("ws_textureuv", ws_textureuv);
            console.AddCommand("ws_type", ws_type);
            console.AddCommand("ws_damage", ws_damage);
            console.AddCommand("ws_bottom", ws_bottom);
            console.AddCommand("ws_top", ws_top);
            console.AddCommand("ws_gravity", ws_gravity);
            console.AddCommand("ws_time", ws_time);
            console.AddCommand("ws_light", ws_light);
            console.AddCommand("ws_entered", ws_entered);
            console.AddCommand("ws_leaved", ws_leaved);
            console.AddCommand("ws_idle", ws_idle);
            console.AddCommand("ws_create", ws_create);
            console.AddCommand("ws_selectwall", ws_selectwall);
        }

        void ws_name(IList<string> args)
        {
            Name = args[1];
        }
        void ws_floor(IList<string> args)
        {
            FloorHeight = float.Parse(args[1]);
        }
        void ws_ceiling(IList<string> args)
        {
            CeilingHeight = float.Parse(args[1]);
        }
        void ws_floortexture(IList<string> args)
        {
            FloorTexture = args[1];
        }
        void ws_ceilingtexture(IList<string> args)
        {
            CeilingTexture = args[1];
        }
        void ws_type(IList<string> args)
        {
            SectorType = (SectorTypes)int.Parse(args[1]);
        }
        void ws_damage(IList<string> args)
        {
            DamagePoints = float.Parse(args[1]);
        }
        void ws_bottom(IList<string> args)
        {
            if (args.Count > 1)
                LowerSector = args[1];
        }
        void ws_top(IList<string> args)
        {
            if (args.Count > 1)
                UpperSector = args[1];
        }
        void ws_gravity(IList<string> args)
        {
            GravityRate = float.Parse( args[1]);
        }
        void ws_time(IList<string> args)
        {
            IdleTime = int.Parse(args[1]);
        }
        void ws_light(IList<string> args)
        {
            LightIntensity = float.Parse(args[1]);
        }
        void ws_entered(IList<string> args)
        {
            OnEnterScript = console.Args2Str(args, 1);
        }
        void ws_leaved(IList<string> args)
        {
            OnLeaveScript = console.Args2Str(args, 1);
        }
        void ws_idle(IList<string> args)
        {
            IdleScript = console.Args2Str(args, 1);
        }
        void ws_create(IList<string> args)
        {
            CreateModel();
        }
        void ws_selectwall(IList<string> args)
        {
            foreach (var item in Walls)
            {
                if (item.Name == args[1])
                {
                    item.AttachToConsole(console);
                    return;
                }
            }
            int wi = int.Parse(args[1]);
            Walls[wi].AttachToConsole(console);
        }
        void ws_textureuv(IList<string> args)
        {
            switch (args[1].ToLower()[0])
            {
                case 'f':
                    FloorUV = GeometryHelper.String2Vector(args[2]);
                    break;
                case 'c':
                    CeilingUV = GeometryHelper.String2Vector(args[2]);
                    break;
                default:
                    console.WriteLine("Invalid Texture Type");
                    return;
            }
        }
        #endregion
    }
}
